﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Web.UI;
using System.Reflection;
using System.CodeDom;
using System.ComponentModel.Design;

namespace MvcImproved.Generic
{
	public class GenericUserControlBuilder : FileLevelUserControlBuilder, IGenericControlBuilder
	{
		/*public override Type GetChildControlType(string tagName, IDictionary attribs)
		{
			Type type = base.GetChildControlType(tagName, attribs);
			GenericControlTypeAttribute data = type.GetCustomAttributes(typeof(GenericControlTypeAttribute), true).Cast<GenericControlTypeAttribute>().FirstOrDefault();
			if (data != null)
			{
				List<Type> genericArgList;
				if (attribs.Contains(data.GenericParameters))
				{
					genericArgList = data.GenericArgumentList(attribs);
				}
				else //Otherwise, try to get the type from the model type 
				{
					genericArgList = new List<Type>();
					genericArgList.Add(ControlType.GetGenericArguments()[0]);
				}
				string lambdaMemberAccess;
				genericArgList.Add(data.GetMemberAccess(genericArgList[genericArgList.Count - 1], attribs, out lambdaMemberAccess));
				type = data.GenericControl.MakeGenericType(genericArgList.ToArray());
			}

			return type;
		}*/

		public string PageBaseTypeName
		{
			get;
			set;
		}

		private static PropertyInfo BaseTypeMember = typeof(TemplateParser).GetProperty("BaseType", BindingFlags.Instance | BindingFlags.NonPublic);
		private static FieldInfo PageParserFilter = typeof(TemplateParser).GetField("_pageParserFilter", BindingFlags.Instance | BindingFlags.NonPublic);

		public override void Init(TemplateParser parser, ControlBuilder parentBuilder, Type type,
			string tagName, string id, IDictionary attribs)
		{
			GenericPageParser parserFilter = PageParserFilter.GetValue(parser) as GenericPageParser;
			if ((parserFilter != null) && (parserFilter._GenericData != null))
			{
				GenericControlTypeAttribute data = parserFilter._GenericData;
				// Set the base type to the generic type
				Type newType = data.GenericControl.MakeGenericType(data.GenericArgumentList(parserFilter._GenericParameters, InDesigner, ServiceProvider).ToArray());

				BaseTypeMember.SetValue(parser, newType, null);
			}
		}

		public override void ProcessGeneratedCode(
			CodeCompileUnit codeCompileUnit,
			CodeTypeDeclaration baseType,
			CodeTypeDeclaration derivedType,
			CodeMemberMethod buildMethod,
			CodeMemberMethod dataBindingMethod)
		{
			// Use a base class string if one was specified by the GenericPageParser
			if (PageBaseTypeName != null)
			{
				derivedType.BaseTypes[0] = new CodeTypeReference(PageBaseTypeName);
			}
		}
	}

	[ControlBuilder(typeof(GenericUserControlInstanceBuilder))]
	[GenericControlType(typeof(GenericViewUserControl<>), MemberValue = "ValueMember", GenericParameters = "ModelType")]
	public class GenericViewUserControl<TDataItem> : System.Web.Mvc.ViewUserControl<TDataItem>, IViewDataContainer<TDataItem>
	{
		protected IControlModelHelper<TDataItem> ValueMemberLambda;

		public string ValueMember
		{
			get;
			set;
		}

		private TDataItem InitModel()
		{
			if (!String.IsNullOrEmpty(ViewDataKey))
				return ViewData.Model;
			else
				return ValueMemberLambda.GetModel(this);
		}

		protected virtual void InitViewData()
		{
			if (DesignMode || (ValueMemberLambda == null))
				return;

			// MVC ViewUserControl doesn't give us a way to make sure the data is set,
			// and throws an exception on get
			try
			{
				if (Model != null)
					return;
				ViewData.Model = InitModel();
			}
			catch (Exception)
			{
				SetViewData(new System.Web.Mvc.ViewDataDictionary<TDataItem>(InitModel()));
			}
		}

		protected override void OnPreRender(EventArgs e)
		{
			base.OnPreRender(e);

			InitViewData();
		}

		protected override void Render(HtmlTextWriter writer)
		{
			InitViewData();

			base.Render(writer);

			if (DesignMode)
				writer.Write(ValueMember);
		}

		protected GenericViewUserControl()
		{}

		// Ideally, this would be in the constructor - unfortunately, TemplateControlCodeDomTreeGenerator
		// does not inject a CodeConstructor into the members of our control until AFTER it calls
		// ControlBuilder.ProcessGeneratedCode
		public void InitValueMember(IControlModelHelper<TDataItem> valueMember)
		{
			ValueMemberLambda = valueMember;
		}
	}

	// Non-generic version of the control that's used in the actual markup
	[FileLevelControlBuilder(typeof(GenericUserControlBuilder))]
	public class ViewUserControl : GenericViewUserControl<object>
	{
		public ViewUserControl()
		{ }

		public string ModelType
		{
			get;
			set;
		}
	}
}
